package co.edu.usbcali.raju.games.chess.helpers;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import co.edu.usbcali.raju.games.BoardWrapper;
import co.edu.usbcali.raju.games.chess.model.ChessBoard;
import co.edu.usbcali.raju.games.chess.model.ChessCell;
import co.edu.usbcali.raju.games.chess.model.ChessPiece;
import co.edu.usbcali.raju.model.Board;
import co.edu.usbcali.raju.model.Cell;
import co.edu.usbcali.raju.model.Move;
import co.edu.usbcali.raju.model.Piece;

import jpl.Atom;
import jpl.Compound;
import jpl.Query;
import jpl.Term;
import jpl.Util;
import jpl.Variable;

public class PlBoardWrapper implements BoardWrapper {

	public static final String BOARD="BOARD";
	public static final String STATUS="STATUS";
	public static final String INITIAL_BOARD="initialBoard("+BOARD+","+STATUS+")";
//	public static final String INITIAL_BOARD="testBoardCMB2("+BOARD+","+STATUS+")";
	public static final String EMPTY="empty";
	public static final String PIECE="piece";
	public static final String SOURCE_ROW="SX";
	public static final String SOURCE_COL="SY";
	public static final String TARGET_ROW="TX";
	public static final String TARGET_COL="TY";
	public static final String GET_MOVE="getMove";
	public static final String KING_CHECK="kingCheck";
	public static final String TARGET_STATUS="TS";
	public static final String TARGET_BOARD="TB";
	public static final String EVALUATE_FORCE_BOARD="evaluateForceBoard";
	public static final String EVALUATE_THREAD_BOARD="evaluateThreatBoard";
	public static final String VALUE="VALUE";
	public static final String CROWNED_PIECE="CP";
	
	private Logger logger=Logger.getLogger(PlBoardWrapper.class.getName());;
	
	private static PlBoardWrapper instance;
	
	public static PlBoardWrapper getInstance(){
		if (instance==null){
			instance=new PlBoardWrapper();
			instance.logger.setLevel(Level.DEBUG);
		}
		return instance;
	}

	public Object exportStatus(Board board) {
		Map<String,Object> status= board.getStatus();
		Set<String> set=status.keySet();
		Term[] terms=new Term[set.size()];
		int con=0;
		for (String string : set) {
			if(string.substring(1).equals("ep")){   //Para la captura al paso, indica la columna del ultimo movimiento en entero
				terms[con]=new jpl.Integer((Integer)status.get(string));
			}else{
				terms[con]=new Compound(string,new Term[]{new Atom((String)status.get(string))});
			}
			con++;
		}
		return Util.termArrayToList(terms);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public Object exportBoard(Board board) {
		List<ChessCell> cells=(List<ChessCell>)board.getCells();
		Term[] rows=new Term[8];
		int row=0;
		Term[] cols=new Term[8];
		int col=0;
		for (ChessCell chessCell : cells) {
//			Term[] cols=new Term[8];
			if(chessCell.getPieces().isEmpty()){
				cols[col]=new Atom(EMPTY);
			}else{
				ChessPiece piece=(ChessPiece)chessCell.getPieces().get(0);
				cols[col]=new Compound(PIECE, new Term[]{new Atom(piece.getType()),new Atom(piece.getPlayer())});
			}
			col++;
			if(col==8){
				rows[row]=Util.termArrayToList(cols);
				col=0;
				cols=new Term[8];
				row++;
			}
		}
		return Util.termArrayToList(rows);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Board importBoard(Object object) {
		Hashtable<String, Compound> hash=(Hashtable<String, Compound>)object;
        Compound tab = (Compound)hash.get(BOARD);
		ChessBoard chessBoard=new ChessBoard();
		List<ChessCell> cells=new ArrayList<ChessCell>();
		Term term[]=tab.toTermArray();
        for (int i = 0; i < ChessBoard.SIDE; i++) {
            Term[] fila = term[i].toTermArray();
            for (int j = 0; j < ChessBoard.SIDE; j++) {
            	if(!fila[j].toString().equals(EMPTY)){
            		ChessPiece piece=(ChessPiece)PlPieceWrapper.getInstance().importPiece(fila[j]);
//            		ChessCell cell=new ChessCell(i,j,piece);
            		ChessCell cell=new ChessCell(i,j);
            		piece.setCell(cell);
            		cells.add(cell);
            	}
            }
        }
        chessBoard.setup(cells);
        Compound status = (Compound)hash.get(STATUS);
		term=status.toTermArray();
		for(int i=0;i<term.length;i++){
			chessBoard.getStatus().put(term[i].name(),term[i].arg(1).toString());
		}
		return (Board)chessBoard;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public Board getInitialBoard(){
		Query query=new Query(INITIAL_BOARD);
		query.open();
        Hashtable<String,Compound> st = query.getSolution();
        query.close();
        return importBoard(st);
	}
	
	
	@SuppressWarnings("unchecked")
	public List<String> getMoves(Board board){
		Term boardS=(Term)exportBoard(board);
		Term statusS=(Term)exportStatus(board);
		Variable rowS=new Variable(SOURCE_ROW);
		Variable colS=new Variable(SOURCE_COL);
		Variable rowT=new Variable(TARGET_ROW);
		Variable colT=new Variable(TARGET_COL);
		Variable statusT=new Variable(TARGET_STATUS);
		Variable boardT=new Variable(TARGET_BOARD);
		Variable crownedP=new Variable(CROWNED_PIECE);
		Compound askMoves=new Compound(GET_MOVE, new Term[]{boardS,statusS,rowS,colS,boardT,statusT,rowT,colT,crownedP,new Atom(((ChessBoard)board).getTurn())});
		logger.debug("Consultando:"+Compound.toString(new Term[]{askMoves}));
		Query query=new Query(askMoves);
		query.open();
     //   Hashtable<String,Compound>[] st = query.allSolutions();
    //    List<Move> moves=new ArrayList<Move>();
	    List<String> moves=new ArrayList<String>();
	        while(query.hasMoreSolutions()){
        	 Hashtable binding = (Hashtable) query.nextElement();
        	 Term rS=(Term)binding.get(SOURCE_ROW);
        	 Term cS=(Term)binding.get(SOURCE_COL);
        	 Term rT=(Term)binding.get(TARGET_ROW);
        	 Term cT=(Term)binding.get(TARGET_COL);
        	 Term bT=(Term)binding.get(TARGET_BOARD);
        	 Term sT=(Term)binding.get(TARGET_STATUS);
        	 Term cP=(Term)binding.get(CROWNED_PIECE);
        	 String ret=""+rS.intValue()+cS.intValue()+rT.intValue()+cT.intValue();
        	 if(cP.toString().charAt(0)!='_'){
        		 ret+=cP.toString();
        	}
        	moves.add(ret); 
//        	 Move move=buildMove((ChessBoard)board,rS.intValue(), cS.intValue(), rT.intValue(), cT.intValue());
//        	 moves.add(move);
        } 
        query.close();
        return moves;
	}
	
/*
	@SuppressWarnings("unchecked")
	public List<Transition> getMoves(Board board){
		Term boardS=(Term)exportBoard(board);
		Term statusS=(Term)exportStatus(board);
		Variable rowS=new Variable(SOURCE_ROW);
		Variable colS=new Variable(SOURCE_COL);
		Variable rowT=new Variable(TARGET_ROW);
		Variable colT=new Variable(TARGET_COL);
		Variable statusT=new Variable(TARGET_STATUS);
		Compound askMoves=new Compound(GET_MOVE, new Term[]{boardS,statusS,rowS,colS,statusT,rowT,colT,new Atom(((ChessBoard)board).getTurn())});
		logger.debug("Consultando:"+Compound.toString(new Term[]{askMoves}));
		Query query=new Query(askMoves);
     //   Hashtable<String,Compound>[] st = query.allSolutions();
        List<Move> moves=new ArrayList<Move>();
        while(query.hasMoreSolutions()){
        	 Hashtable binding = (Hashtable) query.nextElement();
        	 Term rS=(Term)binding.get(SOURCE_ROW);
        	 Term cS=(Term)binding.get(SOURCE_COL);
        	 Term rT=(Term)binding.get(TARGET_ROW);
        	 Term cT=(Term)binding.get(TARGET_COL);
        	 Term sT=(Term)binding.get(TARGET_STATUS);
        	 Move move=buildMove((ChessBoard)board,rS.intValue(), cS.intValue(), rT.intValue(), cT.intValue());
        	 moves.add(move);
        } 
        return moves;
	}
*/
	public double evaluateForce(ChessBoard board){
		Term boardS=(Term)exportBoard(board);
		Variable valueV=new Variable(VALUE);
//		Compound evaluate=new Compound(EVALUATE_FORCE_BOARD, new Term[]{boardS,new Atom(((ChessBoard)board).getPreviousTurn()),statusT});
		Compound evaluate=new Compound(EVALUATE_FORCE_BOARD, new Term[]{boardS,valueV});
		logger.debug("Consultando:"+Compound.toString(new Term[]{evaluate}));
		Query query=new Query(evaluate);
		query.open();
		Hashtable binding=query.getSolution();
		Term value=(Term)binding.get(VALUE);
		query.close();
		return value.doubleValue();
	}
	

	
	public Move buildMove(ChessBoard board,int rs,int cs,int rt,int ct){
		Cell cell=board.getCell(rt, ct);
		Piece piece=board.getCell(rs, cs).getPieces().get(0);
		return new Move(piece,cell);
	}
	
	public boolean isTerminal(ChessBoard board){
		return getMoves(board).isEmpty();
	}
	
	public boolean askForStalemate(ChessBoard board){
		if(isTerminal(board)){
			Term boardS=(Term)exportBoard(board);
			Term statusS=(Term)exportStatus(board);
			Variable rowS=new Variable(SOURCE_ROW);
			Variable colS=new Variable(SOURCE_COL);
			Compound askCheck=new Compound(KING_CHECK, new Term[]{boardS,statusS,new Atom(board.getTurn()),rowS,colS});
			logger.debug("Consultando:"+Compound.toString(new Term[]{askCheck}));
			Query query=new Query(askCheck);
			return query.hasSolution();
		}
		return false;
	}
	

}
